package org.example.util;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Slf4j
public class HttpOkUtil {

    public static final OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(45, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .connectionPool(new ConnectionPool(
                    50, 2, TimeUnit.MINUTES))
            .build();

    public static class BasicAuthInterceptor implements Interceptor {
        private String credentials;


        public BasicAuthInterceptor(String user, String password) {
            this.credentials = Credentials.basic(user, password);
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Request authenticatedRequest = request.newBuilder()
                    .header("Authorization", credentials).build();
            return chain.proceed(authenticatedRequest);
        }
    }

    public String deleteWithJson(String url, Object json, String username, String password) {
        OkHttpClient client = getOkHttpClient(username, password);
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, "");
        Request request = new Request.Builder()
                .url(url)
                .delete(body)
                .addHeader("content-type", "application/json")
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                assert response.body() != null;
                String bodyStr = new String(response.body().bytes());
                log.info("HTTP远程调用》》》 url:{} obj:{}", url, json);
                return bodyStr;
            } else {
                throw new RuntimeException("HTTP远程调用失败，url=" + url + "，obj=" + json + "，response=" + response.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("HTTP远程调用失败，url：" + url + "，obj：" + json, e);
            throw new RuntimeException(e);
        }
    }

    public String postWithJson(String url, Object obj, String username, String password) {
        String json = obj != null ? JSONUtil.toJsonStr(obj) : "";
        OkHttpClient client = getOkHttpClient(username, password);
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .addHeader("content-type", "application/json")
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                assert response.body() != null;
                String bodyStr = new String(response.body().bytes());
                log.info("HTTP远程调用》》》 url:{} obj:{}", url, json);
                return bodyStr;
            } else {

                throw new RuntimeException("HTTP远程调用失败，url=" + url + "，obj=" + json + "，response=" + response.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("HTTP远程调用失败，url：" + url + "，obj：" + json, e);
            throw new RuntimeException(e);
        }
    }

    private static OkHttpClient getOkHttpClient(String admin, String geoserver) {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new BasicAuthInterceptor(admin, geoserver))
                .connectTimeout(45, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(
                        5, 2, TimeUnit.MINUTES))
                .build();
        return client;
    }

    public static File downloadImageToFile(String url, String admin, String geoserver) {
        OkHttpClient okHttpClient = getOkHttpClient(admin, geoserver);
        Request request = new Request.Builder().url(url).build();
        String realFileName = getRealFileName(url);
        try (Response response = okHttpClient.newCall(request).execute();
             FileOutputStream outputStream = new FileOutputStream(realFileName);) {
            assert response.body() != null;
            outputStream.write(response.body().bytes());
            return new File(realFileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从 url 指示的位置异步下载文件流，一般认为 url 返回的都是文件，而不是文本内容。因为本方法为异步方法，调用 url 失败时不会返回异常信息,只是 receiveReturnList 的 index 位置会是你初始化时设置的值，这个值可以是 null，这取决于你初始化 receiveReturnList 时是否都设置的 null。
     *
     * @param url               下载地址
     * @param receiveReturnList 下载完成后接收文件的数组:请确保 list 对象已经经过适当的初始化，传入前可以先对所有位置设置 null，即：receiveReturnList.add(null);这样后续本方法 list.set(index,object) 时才不会报数组下标越界异常
     * @param index             本文件下载完成后，插入到接收数组的哪个位置上
     */
    public static void downloadAsynByte(String url, List<byte[]> receiveReturnList, int index) {
        Request request = new Request.Builder().url(url).addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36").build();
        Call call = okHttpClient.newCall(request);

        call.enqueue(new Callback() {
            public void onResponse(Call arg0, Response arg1) throws IOException {
                if (arg1.isSuccessful() && arg1.body() != null) {
                    receiveReturnList.set(index, arg1.body().bytes());
                } else {
                    log.error("调用文件下载服务失败：连接地址：" + url + "；返回参数：" + JSONObject.toJSON(arg1));
                }
                arg1.close();
            }

            public void onFailure(Call arg0, IOException arg1) {
                log.error("调用文件下载服务失败：连接地址：" + url, arg1);
            }
        });
    }

    public static byte[] downloadByte(String url) {
        Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        try (Response response = call.execute();) {
            if (response.isSuccessful() && response.body() != null) {
                return response.body().bytes();
            } else {
                log.error("调用文件下载服务失败：连接地址：" + url + "；返回参数：" + JSONObject.toJSON(response));
            }
        } catch (Exception e) {
            log.error("调用文件下载服务失败：连接地址：" + url, e);
        }
        return null;
    }


    public static File downloadImg(String url, String fileName) {
        Request request = new Request.Builder().url(url).build();
        String realFileName = fileName;
        if (StrUtil.isBlank(realFileName)) {
            realFileName = getRealFileName(url);
        }

        try (Response response = okHttpClient.newCall(request).execute();) {
            if (response.body() != null) {
                FileUtils.writeByteArrayToFile(new File(realFileName), response.body().bytes());
                return new File(realFileName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String postWithXml(String url, String bodyStr,
                              String username, String password) {
        OkHttpClient client = getOkHttpClient(username, password);
        okhttp3.MediaType mediaType = okhttp3.MediaType.parse("application/xml");
        RequestBody body;
        if (StrUtil.isNotBlank(bodyStr)) {
            body = RequestBody.create(mediaType, bodyStr);
        } else {
            body = RequestBody.create(mediaType, "");
        }

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                assert response.body() != null;
                String res = new String(response.body().bytes());
                log.info("HTTP远程调用 >>> url:{} obj:{}", url, bodyStr);
                return res;
            } else {
                throw new RuntimeException("HTTP远程调用失败，url=" + url + "，obj=" + bodyStr + "，response=" + response.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("HTTP远程调用失败，url：" + url + "，obj：" + body, e);
            throw new RuntimeException(e);
        }
    }

    private static String getRealFileName(String url) {
        String path = getCurPath();
        // jar 文件不在 /tmp 目录里，jpg合格证在 tmp 目录
        if (!url.endsWith(".jar")) {
            path = path + "tmp" + File.separator;
        }
        File file = new File(path);
        if (!file.exists()) {
            file.mkdir();
        }

        String fileName = url.substring(url.lastIndexOf("/") + 1);
        Pattern compile = Pattern.compile("^[a-zA-Z0-9]+[.]*[a-zA-Z0-9]+$");
        if (!compile.matcher(fileName).find()) {
            fileName = RandomUtil.randomString(6) + ".jpg";
        }

        return path + fileName;
    }

    // 最后一个字符是一个 '/'
    public static String getCurPath() {
        String path = HttpUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        if (path.startsWith("file:/")) {
            path = path.replace("file:/", "")
                    .replace("!/BOOT-INF/classes!", "")
                    .replace("/target/classes", "");
            String[] split = path.split("/");
            StringBuilder sb = new StringBuilder();
            for (String s : split) {
                if (!s.endsWith(".jar") && !"".equals(s)) {
                    sb.append(s).append("/");
                }
            }
            path = sb.toString();
        }
        return path;

//        File file = new File(".");
//        return file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - 1);
    }

    public static void downloadImages(String url) {
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");


            DataInputStream dataInputStream = new DataInputStream(conn.getInputStream());
            ;
            FileOutputStream fileOutputStream;
            //根据坐标 x y z生成文件名 下载png无损图片
            String imageName = getRealFileName(url);
            fileOutputStream = new FileOutputStream(new File(imageName));
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = dataInputStream.read(buffer)) > 0) {
                output.write(buffer, 0, length);
            }
            fileOutputStream.write(output.toByteArray());
            dataInputStream.close();
            fileOutputStream.close();
            output.close();
            System.out.println("已完成下载！");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

